#pragma once 

#include <iostream>
#include <queue>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <pthread.h>

#define NUM 5

template<typename T>
class ProcessPool{
  private:
    int process_num;
    pthread_mutex_t lock;
    pthread_cond_t full;
    pthread_cond_t empty;
    std::queue<T> task_queue;
  public:
    ProcessPool(int _num = NUM):process_num(_num)
    { 
      pthread_mutex_init(&lock);
      pthread_cond_init(&full);
      pthread_cond_init(&empty);
    }
    
    void HandlerRequest()
    {
      while(true){
        while(IsEmpty()){
          EmptyWait();
        }
          T t;
          Pop(t);
          t.Run();
      }    
    }


    void InitProcessPool()
    {
      pid_t fds[process_num];
      for(int i = 0; i < process_num; i++){
        fds[i] = fork();
        if(fds[i] == 0){
          //子进程
          HandlerRequest(); 
        }
        //父进程
        wait(nullptr);
      }
    }
    
    void Push(const T& in)
    {
      task_queue.push(in);
      EmptyWakeUp();
    }
    
    void Pop(T& out)
    {
      out = task_queue.front();
      task_queue.pop();
    }

    void Lock()
    {
      pthread_mutex_lock(&lock);
    }

    void UnLock()
    {
      pthread_mutex_unlock(&lock);
    }

    void FullWait()
    {
      pthread_cond_wait(&full, &lock);
    }

    void EmptyWait()
    {
      pthread_cond_wait(&empty, &lock);
    }

    void FullWakeUp()
    {
      pthread_cond_signl(&full);
    }

    void EmptyWakeUp()
    {
      pthread_cond_signal(&empty);
    }

    bool IsEmpty()
    {
      return task_queue.size() == 0;
    }

    bool IsFull()
    {
      return task_queue.size() == process_num;
    }

    ~ProcessPool()
    {
      pthread_mutex_destroy(&lock);
      pthread_cond_destroy(&full);
      pthread_cond_destroy(&empty);
    }
};
